package Analisador;

import Tokens.FabricaToken;
import Tokens.Comentario;
import Tokens.Token;
import Util.Inicializar;
import Util.ListaErro;
import java.util.ArrayList;
import java.util.HashMap;

/**
 * Classe Lexico, Responsável por analisar e definir todos os tokens.
 *
 * @author Danilo
 */
public class Lexico {

    /**
     * Função resposável por escanear a string gerada na tela principal pelo
     * textAreaPrincipal.
     *
     * @param string
     */
    public static void scanner(String string) {

        //Carregando palavras reservadas.
        palavrasReservadas = Inicializar.carregaPalavrasReservadas();
        //Carrega simbolos
        simbolos = Inicializar.carregaSimbolos();

        char aux = 0;
        char caracterAtual;
        final char SINALMAIS = '+', SINALMENOS = '-', PONTOEVIRGULA = ';', PONTO = '.', ABRECHAVES = '{';
        final char FECHACHAVES = '}', ASTERISCO = '*', DOISPONTTOS = ':', BARRA = '/', IGUAL = '=', VIRGULA = ',';
        final char EOF = '|', MENOR = '<', MAIOR = '>', ABREPARENTESES = '(', FECHAPARENTESES = ')';
        int posicaoAutomato = 0, casasDecimais = 0, casasInteiro = 0;
        int linha = 1, coluna = 1, i = 0;

        StringBuilder buffer = new StringBuilder();

        while (i <= string.length()) {
            if (i < string.length()) {
                caracterAtual = string.charAt(i);
            } else {
                caracterAtual = ' ';
            }
            switch (posicaoAutomato) {
                //<editor-fold defaultstate="collapsed" desc=" CASE 0"> 
                //Case 0 -> Posicão inicial do automato. 
                case 0:
                    /*
                     Verifica se o primerio Caracter é um símbolo, numero, letra, final de arquivo.
                     Para cada ocasição, ele passa o caracter para seu estado correspondente no automato.
                     */
                    if (Character.isDigit(caracterAtual)) {
                        posicaoAutomato = 1;
                        buffer.append(caracterAtual);
                        i++;
                        coluna++;
                        break;
                    } else if (Character.isAlphabetic(caracterAtual)) {
                        buffer.append(caracterAtual);
                        i++;
                        coluna++;
                        posicaoAutomato = 12;
                        break;
                    } else if (Character.isWhitespace(caracterAtual)) {
                        if (caracterAtual == '\n') {
                            linha++;
                            coluna = 1;
                            i++;
                        } else {
                            //Caracter em branco, fica no automato zero;
                            i++;
                            coluna++;
                        }
                        break;
                    } else if ((i == string.length())) {
                        i++;
                        break;
                    } else if (simbolos.containsValue(caracterAtual)) {
                        if (Character.compare(caracterAtual, ABRECHAVES) == 0) {
                            //Começo de comentario
                            aux = caracterAtual;
                            posicaoAutomato = 6;
                            buffer.append(caracterAtual);
                            i++;
                            coluna++;
                            break;
                        } else if (Character.compare(caracterAtual, BARRA) == 0) {
                            aux = caracterAtual;
                            buffer.append(caracterAtual);
                            i++;
                            coluna++;
                            posicaoAutomato = 5;
                            break;
                        } else if (Character.compare(caracterAtual, MENOR) == 0) {
                            buffer.append(caracterAtual);
                            i++;
                            coluna++;
                            posicaoAutomato = 10;
                            break;
                        } else if (Character.compare(caracterAtual, MAIOR) == 0) {
                            buffer.append(caracterAtual);
                            i++;
                            coluna++;
                            posicaoAutomato = 16;
                            break;
                        } else if (Character.compare(caracterAtual, IGUAL) == 0) {
                            posicaoAutomato = 18;
                            break;
                        } else if (Character.compare(caracterAtual, PONTOEVIRGULA) == 0) {
                            posicaoAutomato = 19;
                            break;
                        } else if (Character.compare(caracterAtual, ASTERISCO) == 0) {
                            posicaoAutomato = 20;
                            break;
                        } else if (Character.compare(caracterAtual, SINALMENOS) == 0) {
                            posicaoAutomato = 21;
                            break;
                        } else if (Character.compare(caracterAtual, SINALMAIS) == 0) {
                            posicaoAutomato = 22;
                            break;
                        } else if (Character.compare(caracterAtual, DOISPONTTOS) == 0) {
                            buffer.append(caracterAtual);
                            i++;
                            coluna++;
                            posicaoAutomato = 23;
                            break;
                        } else if (Character.compare(caracterAtual, VIRGULA) == 0) {
                            posicaoAutomato = 25;
                            break;
                        } else if (Character.compare(caracterAtual, ABREPARENTESES) == 0) {
                            posicaoAutomato = 26;
                            break;
                        } else if (Character.compare(caracterAtual, FECHAPARENTESES) == 0) {
                            posicaoAutomato = 27;
                            break;
                        } else if (Character.compare(caracterAtual, simbolos.get(17)) == 0) {
                            posicaoAutomato = 28;
                            break;
                        } else if (Character.compare(caracterAtual, simbolos.get(16)) == 0) {
                            posicaoAutomato = 28;
                            break;
                        }
                    } else {
                        //Caracter nao aceito pela linguagem
                        posicaoAutomato = 11;
                    }
                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 1">
                //Case 1: Responsável pelo Inteiro.
                case 1:
                    /*
                     Define se é um numero inteiro, 
                     caso tenha um ponto passa então para posição 2 do automato.
                     */
                    if (Character.isDigit(caracterAtual)) {
                        /*Caracter consumido é um digito então:
                         fica no mesmo estado o automato.
                         */
                        i++;
                        coluna++;
                        buffer.append(caracterAtual);
                        break;

                    } else if (caracterAtual == simbolos.get(17)) {
                        /*Encontrou o divisor de numero,
                         vai para posição 2 do automato.
                         */
                        posicaoAutomato = 2;
                        buffer.append(caracterAtual);
                        i++;
                        coluna++;
                    } else if ((i == string.length() || (Character.isWhitespace(caracterAtual)) || (simbolos.containsValue(caracterAtual)))) {
                        /*Caracter atual é ponto e virgula ou espaço, significa que encontrou 
                         o final do numero. e ele é um inteiro.
                         */
                        posicaoAutomato = 0;
                        adicionaToken(buffer.toString(), NUMEROINTEIRO, linha, coluna - 1);
                        buffer.delete(0, buffer.length());

                    } else {
                        /*Numero mau formado
                         vai para posição 4 do automato                         
                         */
                        posicaoAutomato = 4;
                        buffer.append(caracterAtual);
                    }

                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 2">  
                //Case 2: Lexico de transição entre um numero inteiro e real
                case 2:
                    if (Character.isDigit(caracterAtual)) {
                        /*Se receber um digito,
                         vai posição 3 do automato
                         incremeta i
                         */
                        i++;
                        coluna++;
                        posicaoAutomato = 3;
                        buffer.append(caracterAtual);
                        break;
                    } else if ((i == string.length())) {
                        //Final de arquivo, Erro.
                        posicaoAutomato = 4;
                        break;
                    } else {
                        /*Caracter estranho, numero mau formado
                         vai para posição 4 do automato, tratar o erro.
                         */
                        posicaoAutomato = 4;
                        buffer.append(caracterAtual);
                    }
                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 3">  
                //Case 3: Caso responsável por reconhecer número real.
                case 3:
                    if (Character.isDigit(caracterAtual)) {
                        //Recebe numero, fica na posição atual
                        i++;
                        coluna++;
                        buffer.append(caracterAtual);
                        break;
                    } else if ((i == string.length()) || (Character.isWhitespace(caracterAtual))
                        || simbolos.containsValue(caracterAtual)) {
                        //Final da cadeia reconhecendo o número real.
                        posicaoAutomato = 0;
                        adicionaToken(buffer.toString(), NUMEROREAL, linha, coluna - 1);
                        buffer.delete(0, buffer.length());

                        break;
                    } else {
                        /*numero mau formado,
                         vai para posição 4 do automato
                         */
                        posicaoAutomato = 4;
                        buffer.append(caracterAtual);
                    }
                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 4">
                //Case 4: Caso responsável por reconher erro dos casos de números
                case 4:
                    /*Posição de erro de numero, volta posição do automato para 0, e
                     Retorna numero Mau Formado.*/
                    posicaoAutomato = 0;
                    adicionaToken(buffer.toString(), NUMEROMALFORMADO, linha, coluna);
                    i++;
                    coluna++;
                    buffer.delete(0, buffer.length());

                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 5">  
                case 5:
                    if (Character.compare(caracterAtual, ASTERISCO) == 0) {
                        //Começo de Comentario
                        buffer.append(caracterAtual);
                        posicaoAutomato = 29;
                        i++;
                        coluna++;
                    } else {
                        //Reconhecendo Barra como símbolo
                        posicaoAutomato = 0;
                        adicionaToken(buffer.toString(), SIMBOLO, linha, coluna);
                        buffer.delete(0, buffer.length());
                    }
                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 6"> 
                case 6:
                    //Caso responsável por comentário tudo que estiver nela vair ser considerado comentário
                    //Verifica se é chaves, demilitador de final de arquivo.
                    if (caracterAtual == FECHACHAVES) {
                        //Final de comentario
                        if (aux == ABRECHAVES) {
                            posicaoAutomato = 7;
                        } else {
                            buffer.append(caracterAtual);
                            posicaoAutomato = 9;
                        }
                        break;
                        //Verifica possível começo de final de comentário
//                    } else if (caracterAtual == ASTERISCO) {
//                        posicaoAutomato = 8;
//                        break;
                    } else if (i == string.length()) {
                        //Final de arquivo
                        posicaoAutomato = 9;
                        break;
                    } else if (caracterAtual == '\n') {
                        linha++;
                        coluna = 1;
                        i++;
                        break;
                    } else {
                        //Comentário fica no mesmo posição do automato
                        i++;
                        coluna++;
                        buffer.append(caracterAtual);
                    }

                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 7">  
                case 7:
                    //Caso responsável por final de comentário
                    //Final de comentario 
                    posicaoAutomato = 0;
                    i++;
                    buffer.append(caracterAtual);
                    adicionaToken(buffer.toString(), COMENTARIO, linha, coluna);
                    buffer.delete(0, buffer.length());
                    coluna++;
                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 8">  
                case 8:
                    //Caso intermediario para reconhecer final de comentário finalizando com '*/'.
                    if (caracterAtual == BARRA) {
                        if (aux == BARRA) {
                            //Fim de comentario
                            posicaoAutomato = 7;
                            coluna++;
                            break;
                        } else {
                            buffer.append(caracterAtual);
                            posicaoAutomato = 9;
                        }
                    } else if (caracterAtual == '\n') {
                        i++;
                        coluna = 0;
                        linha++;
                        break;
                    } else if (i == string.length()) {
                        //final de arquivo ERRO
                        posicaoAutomato = 9;
                    } else if (caracterAtual == ASTERISCO) {
                        i++;
                        coluna++;
                        buffer.append(caracterAtual);
                        break;
                        //Caso o proximo caracter não é '/' volta então para o caso de comentário.
                    } else {
                        posicaoAutomato = 29;
                        i++;
                        coluna++;
                        buffer.append(caracterAtual);
                    }
                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 9">
                case 9:
                    //Caso que reconhece final de arquivo e erro de comentário.
                    adicionaToken(buffer.toString(), COMENTARIOMALFORMADO, linha, coluna);
                    i++;
                    coluna++;
                    buffer.delete(0, buffer.length());
                    posicaoAutomato = 0;
                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 10"> 
                case 10:
                    //Caso responsável por reconhecer o símbolo <                 
                    if (Character.compare(caracterAtual, MAIOR) == 0) {
                        buffer.append(caracterAtual);
                        posicaoAutomato = 14;
                    } else if (Character.compare(caracterAtual, IGUAL) == 0) {
                        buffer.append(caracterAtual);
                        posicaoAutomato = 15;
                    } else {
                        posicaoAutomato = 0;
                        adicionaToken(buffer.toString(), SIMBOLO, linha, coluna);
                        buffer.delete(0, buffer.length());
                    }
                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 11">
                case 11:
                    //Caso responsável por reconhecer todos carácter não aceito pelo lingaguem LALG
                    i++;
                    buffer.append(caracterAtual);
                    adicionaToken(buffer.toString(), CARACTERNAOACEITO, linha, coluna);
                    coluna++;
                    buffer.delete(0, buffer.length());
                    posicaoAutomato = 0;
                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 12">  
                case 12:
                    //Caso responsável por reconhecer todas as palavras começando letras a-z.
                    if (Character.isAlphabetic(caracterAtual) || Character.isDigit(caracterAtual)) {
                        buffer.append(caracterAtual);
                        i++;
                        coluna++;
                        //toda cadeia formada é verificada para saber se é palavra reservada, caso nao será considerado um ID
                    } else if ((i == string.length()) || (Character.isWhitespace(caracterAtual)) || (simbolos.containsValue(caracterAtual))) {
                        if (palavrasReservadas.containsValue(buffer.toString())) {
                            posicaoAutomato = 13;
                            break;
                        } else {
                            adicionaToken(buffer.toString(), IDENTIFICADOR, linha, coluna - 1);
                            buffer.delete(0, buffer.length());
                            posicaoAutomato = 0;
                        }
                    } else {
                        //i++;
                        adicionaToken(buffer.toString(), IDENTIFICADOR, linha, coluna - 1);
                        //coluna++;
                        buffer.delete(0, buffer.length());
                        posicaoAutomato = 0;
                    }
                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 13">  
                case 13:
                    adicionaToken(buffer.toString(), PALAVRARESERVADA, linha, coluna - 1);
                    buffer.delete(0, buffer.length());
                    posicaoAutomato = 0;
                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 14"> 
                case 14:
                    //Caso responsável por reconhecer o símbolo '<>' (Diferente)  
                    posicaoAutomato = 0;
                    i++;
                    coluna++;
                    adicionaToken(buffer.toString(), SIMBOLO, linha, coluna);
                    buffer.delete(0, buffer.length());
                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 15"> 
                case 15:
                    //Caso responsável por reconhecer o símbolo '<='  
                    posicaoAutomato = 0;
                    i++;
                    coluna++;
                    adicionaToken(buffer.toString(), SIMBOLO, linha, coluna);
                    buffer.delete(0, buffer.length());
                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 16"> 
                case 16:
                    //Caso responsável por reconhecer o símbolo >                 
                    if (Character.compare(caracterAtual, IGUAL) == 0) {
                        buffer.append(caracterAtual);
                        posicaoAutomato = 17;
                    } else {
                        posicaoAutomato = 0;
                        adicionaToken(buffer.toString(), SIMBOLO, linha, coluna);
                        buffer.delete(0, buffer.length());
                    }
                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 17"> 
                case 17:
                    posicaoAutomato = 0;
                    adicionaToken(buffer.toString(), SIMBOLO, linha, coluna);
                    i++;
                    coluna++;
                    buffer.delete(0, buffer.length());
                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 18"> 
                case 18:
                    buffer.append(caracterAtual);
                    posicaoAutomato = 0;
                    adicionaToken(buffer.toString(), SIMBOLO, linha, coluna);
                    i++;
                    coluna++;
                    buffer.delete(0, buffer.length());
                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 19"> 
                case 19:
                    buffer.append(caracterAtual);
                    posicaoAutomato = 0;
                    adicionaToken(buffer.toString(), SIMBOLO, linha, coluna);
                    i++;
                    coluna++;
                    buffer.delete(0, buffer.length());
                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 20"> 
                case 20:
                    buffer.append(caracterAtual);
                    posicaoAutomato = 0;
                    adicionaToken(buffer.toString(), SIMBOLO, linha, coluna);
                    i++;
                    coluna++;
                    buffer.delete(0, buffer.length());
                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 21"> 
                case 21:
                    buffer.append(caracterAtual);
                    posicaoAutomato = 0;
                    adicionaToken(buffer.toString(), SIMBOLO, linha, coluna);
                    i++;
                    coluna++;
                    buffer.delete(0, buffer.length());
                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 22">
                case 22:
                    buffer.append(caracterAtual);
                    posicaoAutomato = 0;
                    adicionaToken(buffer.toString(), SIMBOLO, linha, coluna);
                    i++;
                    coluna++;
                    buffer.delete(0, buffer.length());
                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 23"> 
                case 23:
                    if (Character.compare(caracterAtual, IGUAL) == 0) {
                        buffer.append(caracterAtual);
                        posicaoAutomato = 24;
                    } else {
                        posicaoAutomato = 0;
                        adicionaToken(buffer.toString(), SIMBOLO, linha, coluna);
                        buffer.delete(0, buffer.length());
                    }
                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 24"> 
                case 24:
                    posicaoAutomato = 0;
                    adicionaToken(buffer.toString(), SIMBOLO, linha, coluna);
                    i++;
                    coluna++;
                    buffer.delete(0, buffer.length());
                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 25"> 
                case 25:
                    buffer.append(caracterAtual);
                    posicaoAutomato = 0;
                    adicionaToken(buffer.toString(), SIMBOLO, linha, coluna);
                    i++;
                    coluna++;
                    buffer.delete(0, buffer.length());
                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 26"> 
                case 26:
                    buffer.append(caracterAtual);
                    posicaoAutomato = 0;
                    adicionaToken(buffer.toString(), SIMBOLO, linha, coluna);
                    i++;
                    coluna++;
                    buffer.delete(0, buffer.length());
                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 27"> 
                case 27:
                    buffer.append(caracterAtual);
                    posicaoAutomato = 0;
                    adicionaToken(buffer.toString(), SIMBOLO, linha, coluna);
                    i++;
                    coluna++;
                    buffer.delete(0, buffer.length());
                    break;
                //</editor-fold>
                //<editor-fold defaultstate="collapsed" desc=" CASE 28"> 
                case 28:
                    buffer.append(caracterAtual);
                    posicaoAutomato = 0;
                    adicionaToken(buffer.toString(), SIMBOLO, linha, coluna);
                    i++;
                    coluna++;
                    buffer.delete(0, buffer.length());
                    break;
                //</editor-fold>  
                //<editor-fold defaultstate="collapsed" desc=" CASE 29">
                case 29:
                    if (caracterAtual == ASTERISCO) {
                        posicaoAutomato = 8;
                        break;
                    } else if (i == string.length()) {
                        //final de arquivo ERRO
                        posicaoAutomato = 9;
                    } else if (caracterAtual == '\n') {
                        i++;
                        coluna = 0;
                        linha++;
                        break;
                    } else {
                        i++;
                        coluna++;
                        buffer.append(caracterAtual);
                        break;
                    }
                //</editor-fold> 
                //<editor-fold defaultstate="collapsed" desc=" CASE 30"> 
                case 30:
                    buffer.append(caracterAtual);
                    posicaoAutomato = 0;
                    adicionaToken(buffer.toString(), SIMBOLO, linha, coluna);
                    i++;
                    coluna++;
                    buffer.delete(0, buffer.length());
                    break;
                //</editor-fold>  

                default:

                    break;
            }
        }
        adicionaToken("FimDeArquivo", FIMDEARQUIVO, linha, coluna);
        //gravarArquivo(arrayTokens);
    }

    /**
     * Função responsável por montar o array de tokens.
     *
     * @param cadeia
     * @param tipo
     * @param linha
     * @param coluna
     */
    private static void adicionaToken(String cadeia, String tipo, Integer linha, Integer coluna) {
        if (tipo.equals(NUMEROMALFORMADO) || tipo.equals(CARACTERNAOACEITO) || tipo.equals(COMENTARIOMALFORMADO)) {
            ListaErro.getInstance();
            Token tk = FabricaToken.returnToken(cadeia, tipo, linha, coluna);
            ListaErro.addLista(LEXICO, tk.toString());
        } else if (tipo.equals(COMENTARIO)) {

        } else {
            getArrayTokens().add(FabricaToken.returnToken(cadeia, tipo, linha, coluna));
        }
    }

    /**
     * Função responsável por montar a String que será usado no textarea na
     * janela principal e no arquivo de saída.
     *
     * @param token
     * @return String
     */
    private static String montarStringdeSaida(ArrayList<Token> token) {
        String texto = new String();
        for (Token tk : token) {
            if (tk instanceof Comentario) {
                continue;
            }
            texto = texto + tk.toString() + "\n";
        }
        return texto;
    }

    /**
     * Função de retornar a String montada com todos os tokens.
     *
     * @return
     */
    public static String retornaToken() {
        String texto = montarStringdeSaida(getArrayTokens());
        getArrayTokens().clear();
        return texto;
    }

    public static Token proximoToken() {
        if (!arrayTokens.isEmpty()) {
            return getArrayTokens().remove(0);
        } else {
            return null;
        }
    }

    public static ArrayList<Token> getArrayTokens() {
        return arrayTokens;
    }

    //Declaração das variáveis.    
    private static HashMap<Integer, String> palavrasReservadas;
    private static HashMap<Integer, Character> simbolos;
    private static final ArrayList<Token> arrayTokens = new ArrayList<>();

    //Variáveis usadas como final para facilitar as comparações
    private static final String CARACTERNAOACEITO = "CaracterNaoAceito";
    private static final String NUMEROMALFORMADO = "NumeroMauFormado";
    private static final String COMENTARIOMALFORMADO = "ComentarioMauFormado";
    private static final String COMENTARIO = "Comentario";
    private static final String IDENTIFICADOR = "Indentificador";
    private static final String NUMEROINTEIRO = "NumeroInteiro";
    private static final String NUMEROREAL = "NumeroReal";
    private static final String SIMBOLO = "Simbolo";
    private static final String PALAVRARESERVADA = "PalavraReservada";
    private static final String LEXICO = "Lexico";
    private static final String FIMDEARQUIVO = "FimDeArquivo";

}
